/*
 * Copyright 2020 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/core/SkBitmap.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkStream.h"
#include "include/encode/SkJpegEncoder.h"
#include "include/encode/SkPngEncoder.h"
#include "include/encode/SkWebpEncoder.h"
#include "include/private/base/SkTFitsIn.h"
#include "include/private/base/SkTo.h"
#include "src/encode/SkImageEncoderPriv.h"
#include "src/image/SkImage_Base.h"
#include "src/ports/SkNDKConversions.h"

static AndroidBitmapInfo info_for_pixmap(const SkPixmap &pmap)
{
    // If any of these values is invalid (e.g. set to zero), the info will be rejected by
    // AndroidBitmap_compress.
    AndroidBitmapInfo info{
        .width = SkTFitsIn<uint32_t>(pmap.width()) ? SkToU32(pmap.width()) : 0,
        .height = SkTFitsIn<uint32_t>(pmap.height()) ? SkToU32(pmap.height()) : 0,
        .stride = SkTFitsIn<uint32_t>(pmap.rowBytes()) ? SkToU32(pmap.rowBytes()) : 0,
        .format = SkNDKConversions::toAndroidBitmapFormat(pmap.colorType())
    };

    switch (pmap.alphaType()) {
        case kPremul_SkAlphaType:
            info.flags = ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
            break;
        case kOpaque_SkAlphaType:
            info.flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
            break;
        case kUnpremul_SkAlphaType:
            info.flags = ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL;
            break;
        default:
            SkDEBUGFAIL("unspecified alphaType");
            info.flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
            break;
    }
    return info;
}

static bool write_image_to_stream(SkWStream *stream, const SkPixmap &pmap, AndroidBitmapCompressFormat androidFormat,
    int quality)
{
    AndroidBitmapInfo info = info_for_pixmap(pmap);

    auto write_to_stream = [](void *userContext, const void *data, size_t size) {
        return reinterpret_cast<SkWStream *>(userContext)->write(data, size);
    };

    return ANDROID_BITMAP_RESULT_SUCCESS == AndroidBitmap_compress(&info,
        SkNDKConversions::toDataSpace(pmap.colorSpace()), pmap.addr(), androidFormat, quality,
        reinterpret_cast<void *>(stream), write_to_stream);
}

namespace SkPngEncoder {
std::unique_ptr<SkEncoder> Make(SkWStream *, const SkPixmap &, const Options &)
{
    SkDEBUGFAIL("Making an encoder is not supported via the NDK");
    return nullptr;
}

bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
{
    return write_image_to_stream(dst, src, ANDROID_BITMAP_COMPRESS_FORMAT_PNG, 100);
}

sk_sp<SkData> Encode(GrDirectContext *ctx, const SkImage *img, const Options &options)
{
    if (!img) {
        return nullptr;
    }
    SkBitmap bm;
    if (!as_IB(img)->getROPixels(ctx, &bm)) {
        return nullptr;
    }
    SkDynamicMemoryWStream stream;
    if (Encode(&stream, bm.pixmap(), options)) {
        return stream.detachAsData();
    }
    return nullptr;
}
} // namespace SkPngEncoder

namespace SkJpegEncoder {
bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
{
    return write_image_to_stream(dst, src, ANDROID_BITMAP_COMPRESS_FORMAT_JPEG, options.fQuality);
}

bool Encode(SkWStream *, const SkYUVAPixmaps &, const SkColorSpace *, const Options &)
{
    SkDEBUGFAIL("encoding a YUVA pixmap is not supported via the NDK");
    return false;
}

sk_sp<SkData> Encode(GrDirectContext *ctx, const SkImage *img, const Options &options)
{
    if (!img) {
        return nullptr;
    }
    SkBitmap bm;
    if (!as_IB(img)->getROPixels(ctx, &bm)) {
        return nullptr;
    }
    SkDynamicMemoryWStream stream;
    if (Encode(&stream, bm.pixmap(), options)) {
        return stream.detachAsData();
    }
    return nullptr;
}

std::unique_ptr<SkEncoder> Make(SkWStream *, const SkPixmap &, const Options &)
{
    SkDEBUGFAIL("Making an encoder is not supported via the NDK");
    return nullptr;
}

std::unique_ptr<SkEncoder> Make(SkWStream *, const SkYUVAPixmaps &, const SkColorSpace *, const Options &)
{
    SkDEBUGFAIL("Making an encoder is not supported via the NDK");
    return nullptr;
}
} // namespace SkJpegEncoder

namespace SkWebpEncoder {
bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
{
    if (options.fCompression == Compression::kLossless) {
        return write_image_to_stream(dst, src, ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS, options.fQuality);
    }
    return write_image_to_stream(dst, src, ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY, options.fQuality);
}

sk_sp<SkData> Encode(GrDirectContext *ctx, const SkImage *img, const Options &options)
{
    if (!img) {
        return nullptr;
    }
    SkBitmap bm;
    if (!as_IB(img)->getROPixels(ctx, &bm)) {
        return nullptr;
    }
    SkDynamicMemoryWStream stream;
    if (Encode(&stream, bm.pixmap(), options)) {
        return stream.detachAsData();
    }
    return nullptr;
}

bool EncodeAnimated(SkWStream *, SkSpan<const SkEncoder::Frame>, const Options &)
{
    SkDEBUGFAIL("Encoding Animated WebP images is not supported with the NDK.");
    return false;
}
} // namespace SkWebpEncoder
